﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Ferpect.ComponentModel.Design;

namespace Ferpect.ComponentModel
{
    public class TransitionScreen : Screen
    {
        private TimeSpan transitionOnTime = TimeSpan.Zero;
        private TimeSpan transitionOffTime = TimeSpan.Zero;
        private float transitionPosition = 0;

        /// <summary>
        /// Gets the current screen transition state.
        /// </summary>
        [Browsable(false)]
        [DefaultValue(ScreenState.Off)]
        public ScreenState ScreenState { get; set; }

        [Category("Behavior")]
        [DefaultValue(0)]
        [Description("Indicates how long the screen takes to transition on when it is activated.")]
        public double TransitionOnSeconds
        {
            get { return this.transitionOnTime.TotalSeconds; }
            set
            {
                if (value < 0.0)
                {
                    throw new ArgumentOutOfRangeException("value");
                }
                this.transitionOnTime = TimeSpan.FromSeconds(value);
            }
        }

        [Category("Behavior")]
        [DefaultValue(0)]
        [Description("Indicates how long the screen takes to transition off when it is deactivated.")]
        public double TransitionOffSeconds
        {
            get { return this.transitionOffTime.TotalSeconds; }
            set
            {
                if (value < 0.0)
                {
                    throw new ArgumentOutOfRangeException("value");
                }
                this.transitionOffTime = TimeSpan.FromSeconds(value);
            }
        }

        /// <summary>
        /// Gets the current position of the screen transition, ranging
        /// from zero (fully active, no transition) to one (transitioned
        /// fully off to nothing).
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public float TransitionPosition
        {
            get { return this.transitionPosition; }
            set { this.transitionPosition = value; }
        }

        /// <summary>
        /// Gets the current alpha of the screen transition, ranging
        /// from 255 (fully active, no transition) to 0 (transitioned
        /// fully off to nothing).
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public byte TransitionAlpha
        {
            get { return (byte)(TransitionPosition * 255); }
        }

        /// <summary>
        /// Helper for updating the screen transition position.
        /// </summary>
        private bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
        {
            // How much should we move by?
            float transitionDelta;

            if (time == TimeSpan.Zero)
            {
                transitionDelta = 1;
            }
            else
            {
                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / time.TotalMilliseconds);
            }

            // Update the transition position.
            this.transitionPosition += transitionDelta * direction;

            // Do we need to continue transitioning?
            if (direction < 0 && this.transitionPosition < Single.Epsilon)
            {
                this.transitionPosition = 0;
                return false;
            }
            if (direction > 0 && this.transitionPosition > (1f - Single.Epsilon))
            {
                this.transitionPosition = 1;
                return false;
            }
            // We need to continue transitioning.
            return true;
        }

        /// <summary>
        /// Update will update the transition position.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            switch (this.ScreenState)
            {
                case ScreenState.TransitioningOn:
                    if (!this.UpdateTransition(gameTime, this.transitionOnTime, 1))
                    {
                        this.ScreenState = ScreenState.On;
                        this.OnTransitionComplete();
                    }
                    break;
                case ScreenState.TransitioningOff:
                    if (!this.UpdateTransition(gameTime, this.transitionOffTime, -1))
                    {
                        this.ScreenState = ScreenState.Off;
                        this.OnTransitionComplete();
                    }
                    break;
            }
            base.Update(gameTime);
        }

        protected virtual void BeginTransition(bool transitionOn)
        {
            if (transitionOn)
            {
                this.ScreenState = ScreenState.TransitioningOn;
            }
            else
            {
                this.ScreenState = ScreenState.TransitioningOff;
            }
        }

        public override void Activate(ScreenContext context)
        {
            this.BeginTransition(true);
            base.Activate(context);
        }

        protected virtual void OnTransitionComplete()
        {
            switch (this.ScreenState)
            {
                case ScreenState.Off:
                    base.Exit();
                    break;
            }
        }

        public override void Exit()
        {
            switch (this.ScreenState)
            {
                case ScreenState.TransitioningOff:
                    break;
                case ScreenState.Off:
                    base.Exit();
                    break;
                case ScreenState.On:
                    this.BeginTransition(false);
                    break;
            }
        }
    }
}
